﻿using PlutoStudio.Data.Common;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Linq;
using System.Linq.Expressions;
using System.Text;

namespace PlutoStudio.Data
{
    public abstract class DataAccessorBase<T> : DataAccessorBase where T : class,new()
    {
        public long Insert(T item)
        {
            return base.Insert(item);
        }

        public long Insert(T item, params Expression<Func<T, object>>[] properties)
        {
            var columns = properties.Select(p => p.GetMember().Member.Name);
            return base.Insert(item, columns.ToArray());
        }

        public int Update(T item)
        {
            return base.Update(item);
        }


        public int Update(T item, params Expression<Func<T, object>>[] properties)
        {
            var columns = properties.Select(p => p.GetMember().Member.Name);
            return base.Update(item, columns.ToArray());
        }

        public T GetByPrimaryKey(PrimaryKey<T> key)
        {
            var result = base.GetByPrimaryKey(key, typeof(T)) as T;
            return result;
        }

        protected DbParameter GenerateDbParameter(T item, Expression<Func<T, object>> property)
        {
            var name = property.GetMember().Member.Name;
            return base.GenerateDbParameter(item, name);
        }



        protected virtual T GetResult(IDataRecord record)
        {
            return base.GetResult(record, typeof(T)) as T;
        }

        internal protected override Type GetEntityType()
        {
            return typeof(T);
        }

        public PrimaryKey<T> GetPrimaryKey()
        {
            return new PrimaryKey<T>();
        }

        protected virtual IEnumerable<T> GetResultSet(IDataReader recordSet)
        {
            var result = new List<T>();
            while (recordSet.Read())
            {
                result.Add(GetResult(recordSet));
            }
            return result;
        }

        protected PropertyItem GetProperty(T item, Expression<Func<T, object>> property)
        {
            var name = property.GetMember().Member.Name;
            return PropertyFactory.GetProperties(typeof(T)).Values.SingleOrDefault(p => p.PropertyName == name);
        }

        public IEnumerable<T> GetEntities()
        {
            var temp = new T();
            var sql = string.Format("SELECT * FROM {0}", GetTableName(temp));
            using (var data = ExecuteReader(GetDatabaseId(temp), sql))
            {
                return GetResultSet(data);
            }
        }

        public IEnumerable<T> GetEntities<TValue>(Expression<Func<T, object>> property, TValue value)
        {
            var temp = new T();
            var p = GetProperty(temp, property);
            p.SetValue(temp, value);
            var where = string.Format("{0}={1}", GenerateDbIdentifier(p), GenerateDBParameterName(p));
            var sql = string.Format("SELECT * FROM {0} WHERE {1}", GetTableName(temp), where);
            var para = GenerateDbParameter(temp, p);
            using (var data = ExecuteReader(GetDatabaseId(temp), sql, para))
            {
                return GetResultSet(data);
            }
        }

        public IEnumerable<T> GetEntities<TValue>(Expression<Func<T, object>> property, TValue value, int start, int count)
        {
            var temp = new T();
            var p = GetProperty(temp, property);
            p.SetValue(temp, value);
            var where = string.Format("{0}={1}", GenerateDbIdentifier(p), GenerateDBParameterName(p));
            var sql = string.Format("SELECT * FROM {0} WHERE {1} LIMIT {2},{3}", GetTableName(temp), where, start, count);
            var para = GenerateDbParameter(temp, p);
            using (var data = ExecuteReader(GetDatabaseId(temp), sql, para))
            {
                return GetResultSet(data);
            }
        }

        public int Count<TValue>(Expression<Func<T, object>> property, TValue value)
        {
            var temp = new T();
            var p = GetProperty(temp, property);
            p.SetValue(temp, value);
            var where = string.Format("{0}={1}", GenerateDbIdentifier(p), GenerateDBParameterName(p));
            var sql = string.Format("SELECT COUNT(0) FROM {0} WHERE {1}", GetTableName(temp), where);
            var para = GenerateDbParameter(temp, p);
            return Convert.ToInt32(ExecuteScalar(GetDatabaseId(temp), sql, para));
        }

        public long Count()
        {
            return Count(typeof(T));
        }
    }
}
